﻿using System;
using IncubusCC.Composites;
using IncubusCC.Dynamics;
using IncubusCC.Helpers;
using IncubusCC.Settings;
using Zeta;
using Zeta.Common.Helpers;
using Zeta.CommonBot;
using Zeta.Internals.Actors;
using Zeta.TreeSharp;
using Action = Zeta.TreeSharp.Action;
using Zeta.Common;

namespace IncubusCC.Routines
{
    public class Wizard
    {
        [Class(ActorClass.Wizard)]
        [Behavior(BehaviorType.Buff)]
        public static Composite WizardBuffs()
        {
            return new PrioritySelector(
                Common.CreateWaitForAttack(),
                Common.CreateWaitForCast(),
                new SelfCast(SNOPower.Wizard_MagicWeapon, extra => !ZetaDia.Me.HasBuff(SNOPower.Wizard_MagicWeapon)),
                new SelfCast(SNOPower.Wizard_Familiar, extra => _familiarTimer.IsFinished, s => _familiarTimer.Reset()),
                new SelfCast(SNOPower.Wizard_EnergyArmor, extra => !ZetaDia.Me.HasBuff(SNOPower.Wizard_EnergyArmor)),
                new SelfCast(SNOPower.Wizard_StormArmor, extra => !ZetaDia.Me.HasBuff(SNOPower.Wizard_StormArmor)),
                new SelfCast(SNOPower.Wizard_IceArmor, extra => !ZetaDia.Me.HasBuff(SNOPower.Wizard_IceArmor))
                );
        }


        [Class(ActorClass.Wizard)]
        [Behavior(BehaviorType.Combat)]
        public static Composite WizardCombat()
        {
            return
                new PrioritySelector(ctx => CombatTargeting.Instance.FirstNpc,
                    new Decorator(ctx => ctx != null,
                        new PrioritySelector(

                    new SelfCast(SNOPower.Wizard_DiamondSkin, extra => ZetaDia.Me.HitpointsCurrentPct <= IncubusCCSettings.Instance.Wizard.DiamondSkinHp || Unit.MeIncapacited),

                    Common.CreateUsePotion(),
                    Common.CreateWaitWhileIncapacitated(),
                    Common.CreateGetHealthGlobe(),
                    Common.CreateWaitForAttack(),
                    Movement.MoveTo(ctx => ((DiaUnit)ctx).Position, 30f),

                    new Decorator(ret => ZetaDia.Me.HasBuff(SNOPower.Wizard_Archon),
                        new PrioritySelector(
                            new SelfCast(SNOPower.Wizard_Archon_SlowTime, extra => ZetaDia.Me.HitpointsCurrentPct <= 0.4),
                            new SelfCast(SNOPower.Wizard_Archon_ArcaneBlast, ctx => Unit.IsElite((DiaUnit)ctx, 16f)
                                || Clusters.GetClusterCount(ZetaDia.Me, CombatTargeting.Instance.LastObjects, ClusterType.Radius, 16f) >= 2),
                            new CastOnUnit(SNOPower.Wizard_Archon_DisintegrationWave, ctx => ((DiaUnit)ctx).ACDGuid),
                            new CastOnUnit(SNOPower.Wizard_Archon_ArcaneStrike, ctx => ((DiaUnit)ctx).ACDGuid)
                            )),

                    // Low health stuff
                    new SelfCast(SNOPower.Wizard_MirrorImage, extra => ZetaDia.Me.HitpointsCurrentPct <= IncubusCCSettings.Instance.Wizard.MirrorImageHp),
                    new SelfCast(SNOPower.Wizard_SlowTime, extra => ZetaDia.Me.HitpointsCurrentPct <= IncubusCCSettings.Instance.Wizard.SlowTimeHp),


                    // AoE spells.
                    new SelfCast(SNOPower.Wizard_WaveOfForce,
                        ctx => Clusters.GetClusterCount(ZetaDia.Me, CombatTargeting.Instance.LastObjects, ClusterType.Radius, 12f) >= 2 ||
                        (Unit.IsElite((DiaUnit)ctx, 16f))),

                    new SelfCast(SNOPower.Wizard_FrostNova,
                        ctx => Clusters.GetClusterCount(ZetaDia.Me, CombatTargeting.Instance.LastObjects, ClusterType.Radius, 16f) >= 2 ||
                                (Unit.IsElite((DiaUnit)ctx, 16f))),

                    //Hydra
                    new CastAtLocation(SNOPower.Wizard_Hydra, ctx => ((DiaUnit)ctx).Position, ctx => _hydraTimer.IsFinished && !Unit.HasPet("Hydra")
                        && Unit.IsElite((DiaUnit)ctx) || Clusters.GetClusterCount((DiaUnit)ctx, CombatTargeting.Instance.LastObjects, ClusterType.Radius, 12f) >= 4,
                        s => _hydraTimer.Reset()),

                    new SelfCast(SNOPower.Wizard_Archon, ctx => Unit.IsElite((DiaUnit)ctx) ||
                        Clusters.GetClusterCount(ZetaDia.Me, CombatTargeting.Instance.LastObjects, ClusterType.Radius, 60f) >= IncubusCCSettings.Instance.Wizard.ArchonAoECount),

                    new CastOnUnit(SNOPower.Wizard_EnergyTwister, ctx => ((DiaUnit)ctx).ACDGuid,
                        ctx => Clusters.GetClusterCount((DiaUnit)ctx, CombatTargeting.Instance.LastObjects, ClusterType.Radius, 20f) >= 2),


                    new CastAtLocation(SNOPower.Wizard_Meteor, ctx => ((DiaUnit)ctx).Position,
                        ctx => _meteorTimer.IsFinished && (Unit.IsElite((DiaUnit)ctx) || 
                            Clusters.GetClusterCount((DiaUnit)ctx, CombatTargeting.Instance.LastObjects, ClusterType.Radius, 16f) >= 4),
                        s => _meteorTimer.Reset()),

                    new CastAtLocation(SNOPower.Wizard_Blizzard, ctx => ((DiaUnit)ctx).Position,
                        ctx => _blizzardTimer.IsFinished && Clusters.GetClusterCount((DiaUnit)ctx, CombatTargeting.Instance.LastObjects, ClusterType.Radius, 16f) >= 4,
                        s => _blizzardTimer.Reset()),

                    new SelfCast(SNOPower.Wizard_ExplosiveBlast,
                        ctx => _explosiveBlast.IsFinished && (Unit.IsElite((DiaUnit)ctx, 12f) ||
                                Clusters.GetClusterCount(ZetaDia.Me, CombatTargeting.Instance.LastObjects, ClusterType.Radius, 12f) >= 2),
                        s => _explosiveBlast.Reset()),

                    // Arcane power spenders.
                    new CastOnUnit(SNOPower.Wizard_ArcaneOrb, ctx => ((DiaUnit)ctx).ACDGuid),
                    new CastOnUnit(SNOPower.Wizard_RayOfFrost, ctx => ((DiaUnit)ctx).ACDGuid),
                    new CastOnUnit(SNOPower.Wizard_ArcaneTorrent, ctx => ((DiaUnit)ctx).ACDGuid),
                    new CastOnUnit(SNOPower.Wizard_Disintegrate, ctx => ((DiaUnit)ctx).ACDGuid),

                    // Signature spells.
                    new CastOnUnit(SNOPower.Wizard_SpectralBlade, ctx => ((DiaUnit)ctx).ACDGuid),
                    new CastOnUnit(SNOPower.Wizard_Electrocute, ctx => ((DiaUnit)ctx).ACDGuid),
                    new CastOnUnit(SNOPower.Wizard_ShockPulse, ctx => ((DiaUnit)ctx).ACDGuid),
                    new CastOnUnit(SNOPower.Wizard_MagicMissile, ctx => ((DiaUnit)ctx).ACDGuid)
                    )),

                   new Action(ret => RunStatus.Success)
                   );
        }

        [Class(ActorClass.Wizard)]
        [Behavior(BehaviorType.Movement)]
        public static Composite WizardMovement()
        {
            return new PrioritySelector(
                    Common.CreateWaitForAttack(),
                    Common.CreateWaitForCast(),
                    new CastAtLocation(SNOPower.Wizard_Teleport,  ctx => (Vector3)ctx, ctx => ZetaDia.Me.Position.Distance((Vector3)ctx) > IncubusCCSettings.Instance.Wizard.TeleportDistance),
                    new SelfCast(SNOPower.Wizard_Archon, ctx => ZetaDia.Me.Position.Distance((Vector3)ctx) > IncubusCCSettings.Instance.Wizard.TeleportDistance),
                    new CastAtLocation(SNOPower.Wizard_Archon_Teleport, ctx => (Vector3)ctx, ctx => ZetaDia.Me.Position.Distance((Vector3)ctx) > IncubusCCSettings.Instance.Wizard.TeleportDistance),
                    new Zeta.TreeSharp.Action((ret) =>
                    {
                        ZetaDia.Me.Movement.MoveActor((Vector3)ret);
                        return RunStatus.Success;
                    })
                );
        }

        #region Timers
        static Wizard()
        {
            GameEvents.OnGameLeft += ResetTimers;
            GameEvents.OnPlayerDied += ResetTimers;
        }

        static void ResetTimers(object sender, EventArgs e)
        {
            _familiarTimer.Stop();
            _blizzardTimer.Stop();
            _hydraTimer.Stop();
            _meteorTimer.Stop();
            _explosiveBlast.Stop();
        }

        private static WaitTimer _explosiveBlast = new WaitTimer(TimeSpan.FromSeconds(5));
        private static WaitTimer _blizzardTimer = new WaitTimer(TimeSpan.FromSeconds(5));
        private static WaitTimer _meteorTimer = new WaitTimer(TimeSpan.FromSeconds(5));
        private static WaitTimer _familiarTimer = new WaitTimer(TimeSpan.FromMinutes(5));
        private static WaitTimer _hydraTimer = new WaitTimer(TimeSpan.FromSeconds(10));

        #endregion


        public static void WizardOnLevelUp(object sender, EventArgs e)
        {
            if (ZetaDia.Me.ActorClass != ActorClass.Wizard)
                return;

            int myLevel = ZetaDia.Me.Level;

            Logger.WriteLog("Player leveled up, congrats! Your level is now: {0}",
                myLevel
                );


            // ********** PRIMARY SLOT CHANGES **********
            // Set Shock Pulse as primary.
            if (myLevel == 3)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_ShockPulse, -1, 0);
                Logger.WriteLog("Setting Shock Pulse as Primary");
            }
            // Set Shock Pulse-Explosive bolts as primary.
            if (myLevel == 9)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_ShockPulse, 1, 0);
                Logger.WriteLog("Changing rune for Shock Pulse: \"Explosive Bolts\"");
            }
            // Set Electrocute as primary.
            if (myLevel == 15)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_Electrocute, -1, 0);
                Logger.WriteLog("Setting Electrocute as Primary");
            }
            // Set Electrocute-Chain lightning as primary.
            if (myLevel == 22)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_Electrocute, 1, 0);
                Logger.WriteLog("Changing rune for Electrocute: \"Chain Lightning\"");
            }

            // ********** SECONDARY SLOT CHANGES **********
            // Set Ray of Frost as secondary spell.
            if (myLevel == 2)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_RayOfFrost, -1, 1);
                Logger.WriteLog("Setting Ray of Frost as Secondary");
            }
            // Set arcane orb as secondary
            if (myLevel == 5)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_ArcaneOrb, -1, 1);
                Logger.WriteLog("Setting Arcane Orb as Secondary");
            }
            // Set arcane orb rune to "obliteration"
            if (myLevel == 11)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_ArcaneOrb, 1, 1);
                Logger.WriteLog("Changing rune for Arcane Orb: \"Obliteration\"");
            }
            // ********** SKILL SLOTS 1-4 **********
            // Set Frost Nova as slot 1
            if (myLevel == 4)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_FrostNova, -1, 2);
                Logger.WriteLog("Setting Frost Nova as slot 1");
            }
            // Set Diamond Skin as slot 1
            if (myLevel == 8)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_DiamondSkin, -1, 2);
                Logger.WriteLog("Setting Diamond Skin as slot 1");
            }
            // Level 9, slot 2 unlocked!
            // Set Wave of Force as slot 2
            if (myLevel == 9)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_WaveOfForce, -1, 3);
                Logger.WriteLog("Setting Wave of Force as slot 2");
            }
            // Level 14, slot 3 unlocked!
            // Set Diamond Skin-Crystal Shell as slot 1, Ice Armor as slot 3
            if (myLevel == 14)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_DiamondSkin, 1, 2);
                Logger.WriteLog("Changing rune for Diamond Skin: \"Crystal Shell\"");
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_IceArmor, -1, 4);
                Logger.WriteLog("Setting Ice Armor as slot 3");
            }
            // Set Wave of Force-Impactful Wave as slot 2
            if (myLevel == 15)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_WaveOfForce, 1, 3);
                Logger.WriteLog("Changing rune for Wave of Force: \"Impactful Wave\"");
            }
            // Level 19, slot 4 unlocked!
            // Set Explosive Blast as slot 4
            if (myLevel == 19)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_ExplosiveBlast, -1, 5);
                Logger.WriteLog("Setting Explosive Blast as slot 4");
            }
            // Set Ice Armor-Chilling Aura as slot 3, Hydra as slot 4
            if (myLevel == 21)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_IceArmor, 1, 4);
                Logger.WriteLog("Changing rune for Ice Armor: \"Chilling Aura\"");
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_Hydra, -1, 5);
                Logger.WriteLog("Setting Hydra as slot 4");
            }
            // Set Hydra-Arcane Hydra as slot 4
            if (myLevel == 26)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_Hydra, 1, 5);
                Logger.WriteLog("Changing rune for Hydra: \"Arcane Hydra\"");
            }
            // Set Energy Armor as slot 3
            if (myLevel == 28)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_EnergyArmor, -1, 4);
                Logger.WriteLog("Setting Energy Armor as slot 3");
            }
            // Set Energy Armor-Absorption as slot 3
            if (myLevel == 32)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_EnergyArmor, 1, 4);
                Logger.WriteLog("Changing rune for Energy Armor: \"Absorption\"");
            }
            // Set Hydra-Venom Hydra as slot 4
            if (myLevel == 38)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_Hydra, 3, 5);
                Logger.WriteLog("Changing rune for Hydra: \"Venom Hydra\"");
            }
            // Set Energy Armor-Pinpoint Barrier as slot 3
            if (myLevel == 41)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_EnergyArmor, 2, 4);
                Logger.WriteLog("Changing rune for Energy Armor: \"Pinpoint Barrier\"");
            }

            // ********** PASSIVE SKILLS **********
            if (myLevel == 10)
            {
                // Blur - Decreases melee damage taken by 20%.
                ZetaDia.Me.SetTraits(SNOPower.Wizard_Passive_Blur);
            }
            if (myLevel == 20)
            {
                // Blur - Decreases melee damage taken by 20%.
                // Prodigy - 4 arcane power from signature casts
                ZetaDia.Me.SetTraits(SNOPower.Wizard_Passive_Blur, SNOPower.Wizard_Passive_Prodigy);
            }
            if (myLevel == 30)
            {
                // Blur - Decreases melee damage taken by 20%.
                // Prodigy - 4 arcane power from signature casts
                // Astral Presence - +20 arcane power, +2 arcane regen
                ZetaDia.Me.SetTraits(SNOPower.Wizard_Passive_Blur, SNOPower.Wizard_Passive_Prodigy, SNOPower.Wizard_Passive_AstralPresence);
            }

        }
    }
}
